package net.softwarefabrik.bde.core.model.project;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * The project classpath used by BDE.
 * 
 * @author Matthias Kuespert
 */
public abstract class ProjectClasspath {

    /**
     * The source directories.
     */
    private Collection<File>              sources              = new ArrayList<File>();

    /**
     * The directory in which class files are written.
     */
    private File                          outputDir            = null;

    /**
     * A map of entries that are defined via a specification container.
     */
    private Map<String, Collection<File>> specificationEntries = new HashMap<String, Collection<File>>();

    /**
     * A map of entries that are defined via a variable.
     */
    private Map<String, Collection<File>> variableEntries      = new HashMap<String, Collection<File>>();

    /**
     * A map of entries that are referring to the workspace.
     */
    private Collection<File>              projects             = new ArrayList<File>();

    /**
     * A map of entries that are referring to an arbitrary location in the
     * filesystem.
     */
    private Collection<File>              files                = new ArrayList<File>();

    /**
     * Adds a source entry.
     * 
     * @param entry The source directory to add.
     */
    protected void addSource(File entry) {
        this.sources.add(entry);
    }

    /**
     * Sets the output directory.
     * 
     * @param outputDir
     */
    protected final void setOutputDir(File outputDir) {
        this.outputDir = outputDir;
    }

    /**
     * Adds a classpath entry for the given variable name.
     * 
     * @param variable The variable name to use as key.
     * @param entry The <code>File</code> to add.
     */
    protected void addVariableEntry(String variable, File entry) {
        Collection<File> entries = this.variableEntries.get(variable);
        if (null == entries) {
            entries = new ArrayList<File>();
        }
        entries.add(entry);
        this.variableEntries.put(variable, entries);
    }

    /**
     * Adds a classpath entry for the given specification name.
     * 
     * @param specification The specification name to use as key.
     * @param entry The <code>File</code> to add.
     */
    protected void addSpecificationEntry(String specification, File entry) {
        Collection<File> entries = this.specificationEntries.get(specification);
        if (null == entries) {
            entries = new ArrayList<File>();
        }
        entries.add(entry);
        this.specificationEntries.put(specification, entries);
    }

    /**
     * Adds a project entry for the given project residing in the workspace.
     * 
     * @param entry The directory of the project to add.
     */
    protected void addProject(File entry) {
        this.projects.add(entry);
    }

    /**
     * Adds a file entry for the given file residing in the filesystem.
     * 
     * @param entry The file to add.
     */
    protected void addFile(File entry) {
        this.files.add(entry);
    }
    
    protected Collection<File> collectEntries() {
        Collection<File> result = new ArrayList<File>();
        //
        result.addAll(this.sources);
        result.add(this.outputDir);
        result.addAll(this.projects);
        for (Collection<File> part : this.variableEntries.values()) {
            result.addAll(part);
        }
        for (Collection<File> part : this.specificationEntries.values()) {
            result.addAll(part);
        }
        result.addAll(this.files);
        return result;
    }

    /**
     * @return A complete list of files on the classpath.
     */
    public File[] getClasspath() {
        //
        return collectEntries().toArray(new File[] {});
    }

    /**
     * @return The source entries.
     */
    public File[] getSourceEntries() {
        return this.sources.toArray(new File[] {});
    }

    /**
     * @return The project entries.
     */
    public File[] getProjectEntries() {
        return this.projects.toArray(new File[] {});
    }

    public String[] getVariables() {
        return this.variableEntries.keySet().toArray(new String[] {});
    }

    /**
     * @param name The name of the variable of which to retrieve entries.
     * @return The variable entries for the given variable name.
     */
    public File[] getVariableEntries(String name) {
        Collection<File> entries = this.variableEntries.get(name);
        if (null == entries) {
            entries = new ArrayList<File>();
        }
        return entries.toArray(new File[] {});
    }

    /**
     * @param name The name of the specification of which to retrieve entries.
     * @return The specification entries for the given specification name.
     */
    public File[] getSpecificationEntries(String name) {
        Collection<File> entries = this.specificationEntries.get(name);
        if (null == entries) {
            entries = new ArrayList<File>();
        }
        return entries.toArray(new File[] {});
    }

    /**
     * @return The file entries.
     */
    public File[] getFileEntries() {
        return this.files.toArray(new File[] {});
    }

    /**
     * @return The output directory.
     */
    public final File getOutputDir() {
        return outputDir;
    }
}
